use std::collections::HashMap;

use log::error;
use oracle::{Connection, Row};
use serde_json::json;
use serde_json::Value;

use crate::datasource::record::Record;
use crate::datasource::{DataSource, DataSourceTypes, IDataSource};
use crate::task::Task;

use super::IDatabase;

pub struct Oracle {
    pub source: DataSourceTypes,
    pub url: String,
    pub username: String,
    pub password: String,
}

impl IDataSource for Oracle {
    fn new(ds: &DataSource) -> Self {
        Oracle {
            source: ds.source,
            url: ds.url.to_string(),
            username: ds.username.to_string(),
            password: ds.password.to_string(),
        }
    }
    fn init(&self, tasks: &HashMap<&String, &Task>) -> Result<(), String> {
        self.init_db(tasks)
    }

    fn ping(&self) -> Result<bool, String> {
        self.conn().query("SELECT 1", &[]).unwrap();
        Ok(true)
    }

    fn records(
        &self,
        task: &Task,
        limit: u32,
        offset: u32,
        scroll_id: u64,
    ) -> Result<Vec<Value>, String> {
        let sql = self.list_records_sql(task, limit, offset, scroll_id);
        let mut records = Vec::new();
        match self.conn().query(sql.as_str(), &[]) {
            Ok(rows) => {
                //read columns
                let cols = rows.column_info();
                println!("{:?}", cols);
                for row_result in rows {
                    if let Ok(row) = row_result {
                        records.push(Oracle::row_to_json(&row));
                    }
                }
            }
            Err(e) => return Err(e.to_string()),
        }
        Ok(records)
    }

    fn tasks(&self, name: &String, task: &Task, count: i32) -> Result<Vec<Record>, String> {
        todo!()
    }

    fn finish(&self, records: &Vec<Record>) -> Result<(), String> {
        let conn = self.conn();
        let mut err = false;
        let mut stmt =
            conn.statement("UPDATE indexea_tasks SET status = :1 WHERE id = :2").build().expect("");
        for rec in records {
            if let Err(e) = stmt.execute(&[&rec.task.status, &rec.task.id]) {
                err = true;
                error!(
                    "update task [id={}] status({}) failed, reason: {:?}",
                    rec.task.id, rec.task.status, e
                )
            }
        }
        if err {
            return Err(String::from("update tasks status failed, Please see the log for details"));
        }
        Ok(())
    }

    fn clean(&self, tasks: &HashMap<&String, &Task>) -> Result<(), String> {
        self.clean_db(tasks)
    }
}

impl IDatabase for Oracle {
    fn tasks_table_exists(&self) -> Result<bool, String> {
        todo!()
    }

    fn create_tasks_table(&self) -> Result<(), String> {
        todo!()
    }

    fn drop_tasks_table(&self) -> Result<(), String> {
        todo!()
    }

    fn create_triggers(&self, name: &String, task: &Task) -> Result<(), String> {
        todo!()
    }

    fn drop_triggers(&self, name: &String, task: &Task) -> Result<(), String> {
        todo!()
    }
}

impl Oracle {
    fn conn(&self) -> Connection {
        Connection::connect(&self.username, &self.password, &self.url).unwrap()
    }

    fn row_to_json(row: &Row) -> Value {
        json!("")
    }
}
